using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.Collections;
using MageDefender.ParticleEngine;


namespace MageDefender.GameObjects
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class EnemyHandler : Microsoft.Xna.Framework.DrawableGameComponent
    {
        public enum Enemies { Slime = 1, Slime2 = 2, Slime3, Slime4, Boss, Ghost, Skeleton, Zombie };
        public enum AI { Forward = 1, Sin = 2, Boss, Ghost };

        AudioEngine engine;
        SoundBank soundBank;
        WaveBank waveBank;
        Cue collideCue;

        ParticleSystem bloodParticles;
        ParticleSystem killEnemyParticles;
        ParticleSystem ghostParticles;

        SpriteBatch spriteBatch;

        List<GameClasses.GameModel> enemyModels;
        List<GameClasses.AnimatedGameModel> animatedEnemyModels;
        List<GameClasses.Enemy> enemies;

        // Boss realted, or to enemies that are shooting/spawning stuff.
        bool addOneEnemyThisFrame = false;
        Enemies enemyToAdd = Enemies.Slime;
        Vector3 addEnemyAtPos = new Vector3();
        float spitTimer;
        // EOF Boss related

        float moveBoss = 0.0f;

        public List<GameClasses.Enemy> EnemiesList
        {
            get { return enemies; }
            set { enemies = value; }
        }

        List<Enemies> spawnEnemyTypes;

        public List<Enemies> SpawnEnemyTypes
        {
            get { return spawnEnemyTypes; }
            set { spawnEnemyTypes = value; }
        }

        float spawnNewEnemy;
        Interfaces.IPlayer player;
        Interfaces.IFort castle;

        public float SpawnNewEnemy
        {
            get { return spawnNewEnemy; }
            set { spawnNewEnemy = value; }
        }

        public void ClearEnemies()
        {
            enemies.Clear();
        }

        public bool AllEnemiesDead
        {
            get
            {
                if (enemies.Count <= 0)
                {
                    return true;
                }
                return false;
            }
        }

        private void GenerateEnemies()
        {
            // Static
            GameClasses.GameModel slimeModel = new GameClasses.GameModel(this.Game);
            slimeModel.SetFileProperties("Models\\blob", "Models\\slime", "Textures\\Toon");
            enemyModels.Add(slimeModel);

            GameClasses.GameModel slime1Model = new GameClasses.GameModel(this.Game);
            slime1Model.SetFileProperties("Models\\blob", "Models\\slime", "Textures\\Toon");
            enemyModels.Add(slime1Model);

            GameClasses.GameModel slime2Model = new GameClasses.GameModel(this.Game);
            slime2Model.SetFileProperties("Models\\blob", "Models\\slime", "Textures\\Toon");
            enemyModels.Add(slime2Model);

            GameClasses.GameModel slime3Model = new GameClasses.GameModel(this.Game);
            slime3Model.SetFileProperties("Models\\blob_fat", "Models\\slime", "Textures\\Toon");
            enemyModels.Add(slime3Model);

            GameClasses.GameModel slimeboss1Model = new GameClasses.GameModel(this.Game);
            slimeboss1Model.SetFileProperties("Models\\blob_boss", "Models\\slime", "Textures\\Toon");
            enemyModels.Add(slimeboss1Model);


            // Animated
            GameClasses.AnimatedGameModel ghostModel = new GameClasses.AnimatedGameModel(this.Game);
            ghostModel.SetFileProperties("Models\\ghost", "Models\\ghost_tex", "Textures\\Toon");
            animatedEnemyModels.Add(ghostModel);

            GameClasses.AnimatedGameModel skeletonModel = new GameClasses.AnimatedGameModel(this.Game);
            skeletonModel.SetFileProperties("Models\\skeleton", "Models\\skeleton_tex", "Textures\\Toon");
            animatedEnemyModels.Add(skeletonModel);

            GameClasses.AnimatedGameModel zombieModel = new GameClasses.AnimatedGameModel(this.Game);
            zombieModel.SetFileProperties("Models\\zombie", "Models\\zombie_tex", "Textures\\Toon");
            animatedEnemyModels.Add(zombieModel);

        }

        public void AddEnemy(Vector3 pos, Enemies e)
        {
            //pos.Y += 300;
            switch (e)
            {
                case Enemies.Slime:
                    {
                        GameClasses.Enemy slimeEnemy = new GameClasses.Enemy("Slime", false, 0, 10, 10, new Vector2(1, 1), 100, pos, AI.Forward, 10, 10, 100);
                        enemies.Add(slimeEnemy);

                        break;
                    }
                case Enemies.Slime2:
                    {
                        GameClasses.Enemy slimeEnemy = new GameClasses.Enemy("Slime2", false, 1, 10, 10, new Vector2(1, 3), 100, pos, AI.Sin, 15, 15, 200);
                        enemies.Add(slimeEnemy);

                        break;
                    }
                case Enemies.Slime3:
                    {
                        GameClasses.Enemy slimeEnemy = new GameClasses.Enemy("Slime3", false, 2, 20, 20, new Vector2(1, 2), 100, pos, AI.Forward, 20, 20, 300);
                        enemies.Add(slimeEnemy);

                        break;
                    }
                case Enemies.Slime4:
                    {
                        GameClasses.Enemy slimeEnemy = new GameClasses.Enemy("Slime4", false, 3, 30, 20, new Vector2(1, 2), 100, pos, AI.Sin, 25, 25, 400);
                        enemies.Add(slimeEnemy);

                        break;
                    }
                case Enemies.Boss:
                    {
                        GameClasses.Enemy boss1Enemy = new GameClasses.Enemy("Crazy Slime", false, 4, 1600, 10, new Vector2(0.2f, 0.2f), 100, pos, AI.Boss, 1000, 400, 2000);
                        boss1Enemy.ImuneAlignments.Add(GameClasses.Spell.Alignments.Slow);
                        boss1Enemy.ImuneAlignments.Add(GameClasses.Spell.Alignments.Poison);
                        enemies.Add(boss1Enemy);

                        break;
                    }
                case Enemies.Ghost:
                    {
                        GameClasses.Enemy slimeEnemy = new GameClasses.Enemy("Ghost", true, 0, 40, 60, new Vector2(1, 2), 100, pos, AI.Ghost, 40, 40, 400);
                        enemies.Add(slimeEnemy);

                        break;
                    }
                case Enemies.Skeleton:
                    {
                        GameClasses.Enemy slimeEnemy = new GameClasses.Enemy("Skeleton", true, 1, 120, 40, new Vector2(1, 2), 100, pos, AI.Forward, 80, 50, 400);
                        enemies.Add(slimeEnemy);

                        break;
                    }

                case Enemies.Zombie:
                    {
                        GameClasses.Enemy slimeEnemy = new GameClasses.Enemy("Zombie", true, 2, 160, 50, new Vector2(1, 2), 100, pos, AI.Forward, 100, 60, 400);
                        enemies.Add(slimeEnemy);

                        break;
                    }
            }
        }

        public EnemyHandler(Game game)
            : base(game)
        {
            // TODO: Construct any child components here
            engine = new AudioEngine("Content\\music.xgs");
            soundBank = new SoundBank(engine, "Content\\SoundBank.xsb");
            waveBank = new WaveBank(engine, "Content\\WaveBank.xwb");

            collideCue = soundBank.GetCue("collide");


            enemyModels = new List<GameClasses.GameModel>();
            animatedEnemyModels = new List<GameClasses.AnimatedGameModel>();
            enemies = new List<GameClasses.Enemy>();
            spawnEnemyTypes = new List<Enemies>();

            killEnemyParticles = new ExplosionParticleSystem(game, game.Content);
            bloodParticles = new BloodParticleSystem(game, game.Content);
            ghostParticles = new GhostParticleSystem(game, game.Content);

            spawnNewEnemy = 1000;
            spitTimer = 2000;

            GenerateEnemies();
        }

        public void IsCollidingWithSpell(GameObjects.SpellHandler spellHandler)
        {
            foreach (GameClasses.Enemy e in enemies)
            {
                foreach (GameClasses.Spell s in spellHandler.SpellList)
                {
                    if (e.IsColliding(s))
                    {
                        if (s.Active)
                        {
                            // is player immune to the spells alignment?
                            bool isImune = false;
                            foreach (GameClasses.Spell.Alignments immune in e.ImuneAlignments)
                            {
                                if (immune == s.Alignment)
                                {
                                    isImune = true;
                                }
                            }

                            if (!isImune)
                            {
                                switch (s.Alignment)
                                {
                                    case GameClasses.Spell.Alignments.Damage:
                                        {
                                            e.Health -= Convert.ToInt32((s.BaseDamage * (player.Intelligence * 0.1f)));
                                            break;
                                        }
                                    case GameClasses.Spell.Alignments.Poison:
                                        {

                                            e.AlignmentsList.Add(GameClasses.Spell.Alignments.Poison);
                                            break;
                                        }
                                    case GameClasses.Spell.Alignments.Slow:
                                        {
                                            e.AlignmentsList.Add(GameClasses.Spell.Alignments.Slow);
                                            break;
                                        }
                                }
                            }

                            if (!s.DestoryOnHit)
                            {
                                s.BaseDamage = 0;
                                s.Active = false;
                            }
                        }

                        if (e.Health <= 0)
                        {
                            e.Active = false;
                            if (!s.DestoryOnHit)
                            {
                                s.Active = false;
                            }
                            player.Score += e.Score;
                            player.Xp += e.Xp;
                            player.Gold += e.Loot;
                        }
                    }
                }
            }
        }
        public void IsCollidingWithPlayer(Vector3 playerPos)
        {
            foreach (GameClasses.Enemy e in enemies)
            {
                if (e.IsColliding(playerPos))
                {
                    e.Active = false;
                    player.Health -= e.BaseDamage;
                }
            }
        }

        public void ApplyAlignments()
        {
            foreach (GameClasses.Enemy e in enemies)
            {
                // Apply all alignments on enemy
                foreach (GameClasses.Spell.Alignments a in e.AlignmentsList)
                {
                    if (a == GameClasses.Spell.Alignments.Slow)
                    {
                        e.Speed *= 0.5f;
                    }
                    else if (a == GameClasses.Spell.Alignments.Poison)
                    {
                        e.IsDOT = true;
                    }
                }

                // is enemy under a Damage over time effect?
                if (e.IsDOT)
                {
                    e.Health -= (player.Intelligence * 0.1f) * 0.1f;
                }

                // is player dead after DOT?
                if (e.Health <= 0)
                {
                    e.Active = false;
                    player.Score += e.Score;
                    player.Xp += e.Xp;
                    player.Gold += e.Loot;
                }

                // All alignments are added, clear list
                e.AlignmentsList.Clear();
            }


        }

        public void IsSpawnNewEnemy(GameTime gameTime)
        {
            spawnNewEnemy -= gameTime.ElapsedGameTime.Milliseconds;
            spitTimer -= gameTime.ElapsedGameTime.Milliseconds;

            if (addOneEnemyThisFrame && spitTimer <= 0)
            {
                spitTimer = 500;
                addOneEnemyThisFrame = false;
                this.AddEnemy(new Vector3(addEnemyAtPos.X, 0, addEnemyAtPos.Z), enemyToAdd);
            }
            addOneEnemyThisFrame = false;

            if (spawnNewEnemy <= 0 && castle.TheCastle.EnemiesInCurrentWave > 0 && SpawnEnemyTypes.Count > 0)
            {
                Random rndEnemyFromList = new Random(gameTime.TotalRealTime.Milliseconds);
                int currentEnemyToSpawn = rndEnemyFromList.Next(0, SpawnEnemyTypes.Count);

                // Random from enemy spawn list
                Random rndSpawnPosition = new Random(gameTime.TotalRealTime.Milliseconds);

                // Are we spawning a boss or...
                if (castle.TheCastle.IsCurrentWaveABoss)
                {
                    this.AddEnemy(new Vector3(rndSpawnPosition.Next(0, 0), 0, 9000), SpawnEnemyTypes[currentEnemyToSpawn]);
                } // A normal enemy?
                else this.AddEnemy(new Vector3(rndSpawnPosition.Next(-1500, 1500), 0, 9000), SpawnEnemyTypes[currentEnemyToSpawn]);

                Random spawnNewEnemyR = new Random();

                spawnNewEnemy = spawnNewEnemyR.Next(800 - ((castle.TheCastle.Level) * 20), 3000 - ((castle.TheCastle.Level) * 20));
                if (spawnNewEnemy <= 100)
                    spawnNewEnemy = 100;

                castle.TheCastle.EnemiesInCurrentWave--;
            }
        }

        public void MoveEnemies(GameTime gameTime, Vector3 vecEye, Matrix viewMatrix)
        {
            int itemsToDelete = 0;

            foreach (GameClasses.Enemy s in enemies)
            {
                if (s.Active)
                {
                    switch (s.AI)
                    {
                        case AI.Forward:
                            {
                                s.PositionZ -= (s.Speed * (gameTime.ElapsedGameTime.Milliseconds)) / 4;
                                s.Update(vecEye, viewMatrix);
                                break;
                            }
                        case AI.Sin:
                            {
                                s.PositionZ -= (s.Speed * (gameTime.ElapsedGameTime.Milliseconds)) / 4;
                                s.PositionX += (float)(Math.Sin(s.PositionZ / 1000) * s.Speed);
                                s.Update(vecEye, viewMatrix);
                                break;
                            }
                        case AI.Boss:
                            {
                                float speedModifier = 1.0f;

                                if (s.PositionZ >= 4000)
                                {
                                    speedModifier = 4.0f;
                                }

                                moveBoss += (gameTime.ElapsedGameTime.Milliseconds) / 10;
                                s.PositionZ -= (speedModifier)*((s.Speed * (gameTime.ElapsedGameTime.Milliseconds)) / 4);
                                if (s.PositionZ <= 2000)
                                {
                                    s.PositionZ = 2000;
                                }
                                s.PositionX = (float)(Math.Sin((moveBoss/100)) * 4*400);

                                if ((s.PositionX > (player.Position.X-50)) && (s.PositionX < (player.Position.X+50)))
                                {
                                    addOneEnemyThisFrame = true;
                                    addEnemyAtPos = s.Position;
                                    enemyToAdd = Enemies.Slime3;
                                }

                                s.Update(vecEye, viewMatrix);
                                break;
                            }
                        case AI.Ghost:
                            {
                                s.PositionZ -= (s.Speed * (gameTime.ElapsedGameTime.Milliseconds)) / 4;
                                s.PositionX += (float)(Math.Sin(s.PositionZ / 1000) * s.Speed);


                                // add particles to ghost
                                for (int i = 0; i < 1; i++)
                                {
                                    ghostParticles.AddParticle(new Vector3(s.PositionX * 0.008f, s.PositionY * 0.008f+1, s.PositionZ * 0.008f), Vector3.Zero);
                                }


                                s.Update(vecEye, viewMatrix);
                                break;
                            }
                    }
                    if (s.Position.Z <= -1000)
                    {

                        castle.TheCastle.Level -= 1;
                        for (int i = 0; i < 50; i++)
                        {
                            killEnemyParticles.AddParticle(new Vector3(s.PositionX * 0.008f, s.PositionY * 0.008f, s.PositionZ * 0.008f), Vector3.Up);
                        }
                        s.Active = false;
                    }
                    if (s.Position.X >= 1500)
                        s.PositionX = 1500;

                    if (s.Position.X <= -1500)
                        s.PositionX = -1500;
                }
                else
                {
                    itemsToDelete++;
                }
            }

            while (itemsToDelete > 0)
            {
                int index = -1;
                foreach (GameClasses.Enemy s in enemies)
                {
                    index++;
                    if (!s.Active)
                    {
                        if (s.AI == AI.Boss)
                        {
                            for (int i = 0; i < 50; i++)
                            {
                                killEnemyParticles.AddParticle(new Vector3(s.PositionX * 0.008f, s.PositionY * 0.008f, s.PositionZ * 0.008f), new Vector3(0,10,0));
                                bloodParticles.AddParticle(new Vector3(s.PositionX * 0.008f, s.PositionY * 0.008f, s.PositionZ * 0.008f), Vector3.Zero);
                            }
                        }
                        else
                        {
                            for (int i = 0; i < 10; i++)
                            {
                                killEnemyParticles.AddParticle(new Vector3(s.PositionX * 0.008f, s.PositionY * 0.008f, s.PositionZ * 0.008f), Vector3.Zero);
                                bloodParticles.AddParticle(new Vector3(s.PositionX * 0.008f, s.PositionY * 0.008f+2, s.PositionZ * 0.008f), Vector3.Zero);
                            }
                        }

                        collideCue = soundBank.GetCue("collide");
                        if (!collideCue.IsPlaying)
                        {
                            collideCue.Play();
                        }

                        break;
                    }
                }
                enemies.RemoveRange(index, 1);
                itemsToDelete--;
            }
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public void Initialize(Matrix projMatrix, Matrix viewMatrix)
        {
            // TODO: Add your initialization code here
            //sceneryModel.Initialize("Models\\plane", "Models\\grass_tex", "Models\\grass_tex", projMatrix, viewMatrix);
            foreach (GameClasses.GameModel e in enemyModels)
            {
                e.Initialize(projMatrix, viewMatrix);
            }

            foreach (GameClasses.AnimatedGameModel a in animatedEnemyModels)
            {
                a.Initialize(projMatrix, viewMatrix);
                a.TheModel.AnimationPlayer.StartClip(a.TheModel.SkinningData.AnimationClips["Start"]);
            }

            killEnemyParticles.Initialize();
            bloodParticles.Initialize();
            ghostParticles.Initialize();

            player = (Interfaces.IPlayer)Game.Services.GetService(typeof(Interfaces.IPlayer));
            castle = (Interfaces.IFort)Game.Services.GetService(typeof(Interfaces.IFort));

            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            base.LoadContent();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public void Update(GameTime gameTime, Vector3 vecEye, Matrix viewMatrix, Matrix projMatrix, GameObjects.SpellHandler sH, Vector3 pP)
        {
            engine.Update();

            this.IsCollidingWithSpell(sH);
            this.IsCollidingWithPlayer(pP);
            this.ApplyAlignments();
            this.IsSpawnNewEnemy(gameTime);
            this.MoveEnemies(gameTime, vecEye, viewMatrix);

            killEnemyParticles.SetCamera(viewMatrix, projMatrix);
            bloodParticles.SetCamera(viewMatrix, projMatrix);
            ghostParticles.SetCamera(viewMatrix, projMatrix);


            killEnemyParticles.Update(gameTime);
            bloodParticles.Update(gameTime);
            ghostParticles.Update(gameTime);

            base.Update(gameTime);
        }

        public int FindNumberOfEnemiesWithModelID(int id)
        {
            int numberOfThisType = 0;
            foreach (GameClasses.Enemy sb in enemies)
            {
                if (sb.Active)
                {
                    if (sb.Animated)
                    {
                        if (sb.ModelID == id)
                        {
                            numberOfThisType++;
                        }
                    }
                }
            }
            return numberOfThisType;
        }

        public override void Draw(GameTime gameTime)
        {
            foreach (GameClasses.Enemy s in enemies)
            {
                if (s.Active)
                {
                    if (s.Animated)
                    {
                        TimeSpan ts = new TimeSpan(gameTime.ElapsedGameTime.Hours, gameTime.ElapsedGameTime.Minutes, gameTime.ElapsedGameTime.Minutes, gameTime.ElapsedGameTime.Seconds, gameTime.ElapsedGameTime.Milliseconds / FindNumberOfEnemiesWithModelID(s.ModelID));
                        animatedEnemyModels[s.ModelID].Update(s.VecEye, s.ViewMatrix, s.WorldMatrix, gameTime, ts);
                        animatedEnemyModels[s.ModelID].Draw(gameTime);
                    }
                    else
                    {
                        enemyModels[s.ModelID].Update(s.VecEye, s.ViewMatrix, s.WorldMatrix, gameTime);
                        enemyModels[s.ModelID].Draw(gameTime);
                    }
                }
            }

            base.Draw(gameTime);
        }

        public void DrawWithParticles(GameTime gameTime)
        {
            spriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.SaveState);

            killEnemyParticles.Draw(gameTime);
            bloodParticles.Draw(gameTime);
            ghostParticles.Draw(gameTime);

            spriteBatch.End();

            this.Draw(gameTime);
        }

        public void DrawParticlesOnly(GameTime gameTime)
        {
            //spriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.SaveState);

            bloodParticles.Draw(gameTime);
            ghostParticles.Draw(gameTime);
            killEnemyParticles.Draw(gameTime);
            //spriteBatch.End();

            //base.Draw(gameTime);
        }
    }
}